package com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicReference;

import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.evaler.BaseJSEvaluator.VariableType;
import com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.global.BaseJSConstants;

public class BaseJSVariable extends BaseJSObject implements BaseJSConstants {
	protected final AtomicReference<Object> value = new AtomicReference<Object>(); // TODO: Use undefined by default.
	private final VariableType type;

	public BaseJSVariable(final String name) {
		super(name);
		this.type = VariableType.REGULAR;
	}

	public BaseJSVariable(final String name, final Object value) {
		super(name);
		this.value.lazySet(value);
		this.type = VariableType.REGULAR;
	}

	public BaseJSVariable(final String name, final VariableType type) {
		super(name);
		this.type = type;
	}

	public BaseJSVariable(final String name, final Object value, final VariableType type) {
		super(name);
		this.value.lazySet(value);
		this.type = type;
	}

	@Override
	public String toString() {
		return "BaseJSVariable[name: " + name + ", value: " + getValueAsString() + "]";
	}

	private String getValueAsString() {
		if (value.get() instanceof int[])
			return Arrays.toString((int[]) value.get());
		else if (value.get() instanceof Object[])
			return Arrays.toString((Object[]) value.get());
		return String.valueOf(value.get());
	}

	public boolean isArray() {
		if (this.value.get() == null || this.value.get() == UNDEFINED)
			return false;
		return ArrayUtils.isArray(this.value.get());
	}

	public int length() {
		if (this.value.get() instanceof Object[])
			return ((Object[]) this.value.get()).length;
		else if (isArray())
			return Array.getLength(value.get());
		else if (this.value.get() instanceof String)
			return ((String) this.value.get()).length();
		throw new UnexpectedObjectTypeError("Unknown object type: " + getValueAsString());
	}

	public void set(final Object o) {
		this.value.lazySet(o);
	}

	public Object get() {
		return this.value.get();
	}

	public boolean isString() {
		if (this.value.get() == null || this.value.get() == UNDEFINED)
			return false;
		return this.value.get() instanceof String;
	}

	public int tryInt() {
		if (this.value.get() instanceof String) {
			return Integer.parseInt((String) value.get());
		} else if (this.value.get() instanceof Integer) {
			return ((int) this.value.get());
		} else if (this.value.get() instanceof Double) {
			return (int) ((double) this.value.get());
		}
		throw new UnexpectedObjectTypeError();
	}

	public boolean isObject() {
		return this.value.get() instanceof BaseJSObject;
	}

	public boolean isVariable() {
		return this.value.get() instanceof BaseJSVariable;
	}

	public void nullPtrCheck() {
		if (this.value.get() == null || this.value.get() == UNDEFINED)
			throw new NullPointerException(name + " == null!");
	}

	public boolean isNumber() {
		return this.value.get() instanceof Number;
	}

	public VariableType getType() {
		return this.type;
	}
}
